<?php
if (!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');

/*
 RCDevs OpenOTP Plugin for SugarCRM v1.0.1
 Copyright (c) 2010-2012 RCDevs, All rights reserved.
 
 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.
 
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
  
 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

require_once('modules/Users/authentication/SugarAuthenticate/SugarAuthenticateUser.php');
require(dirname(__FILE__).'/config.php');

class OpenOTPAuthenticateUser extends SugarAuthenticateUser {
	
	public  $openotp_auth = NULL;
    private $state = NULL;
    private $message = NULL;
    private $timeout = NULL;
    private $domain = NULL;
    private $username = NULL;
    private $password = NULL;
    private $u2f = NULL;
    
    function loadUserOnLogin($name, $password) {
		require_once('modules/Users/authentication/OpenOTPAuthenticate/openotp.class.php');
		
		/*TODO get per user external_auth_only setting to exit OpenOTPAuthenticate class because of Sugar strange behavior: still run OpenOTPAuthenticate even 
		if chechboxe unchecked in admin/user management/[user]/advanced "Authenticate this user only through OpenOTPAuthenticate" 	
		require_once('modules/UserPreferences/UserPreference.php');
		$usr = new user();
		$usr_id = $usr->retrieve_user_id($name);
		$usr->retrieve($usr_id);
		$external_auth_only = $usr->getPreference('external_auth_only');*/	

		// System configuration.
		$openotp_config = new openotp_config;
		$this->openotp_auth = new openotp($this, $openotp_config, dirname(__FILE__));
				
		// Check if config files are available		
		if (!$this->openotp_auth->checkFile('config.php')){
			$GLOBALS['log']->error('No OpenOTP config file found'.dirname(__FILE__));
			return false;
		}
		if (!$this->openotp_auth->checkFile('openotp.wsdl')){
			$GLOBALS['log']->error('Could not load OpenOTP WSDL file');
			return false;
		}
		// Check SOAP extension is loaded
		if (!$this->openotp_auth->checkSOAPext()){
			$GLOBALS['log']->error('Your PHP installation is missing the SOAP extension');
			return false;
		}
		// require server_url
		if (!$this->openotp_auth->getServer_url()) {
			$GLOBALS['log']->error('OpenOTP server URL is not configured');
			return false;
		}
		
		$this->username = $_POST['openotp_username'] != NULL ? $_POST['openotp_username'] : $name;
		$this->password = $_POST['openotp_password'] != NULL ? $_POST['openotp_password'] : $password;
		$this->domain = $_POST['openotp_domain'] != NULL ? $_POST['openotp_password'] : $password;
		$this->u2f = $_POST['openotp_u2f'] != NULL ? $_POST['openotp_u2f'] : "";
		$this->state = $_POST['openotp_state'];

		$this->u2f = html_entity_decode( $this->u2f, ENT_QUOTES );

        if (empty($this->username)) return false;
	
        $GLOBALS['log']->info("Starting OpenOTPAuthenticate authentication for ".$this->username);
		
		$t_domain = $this->openotp_auth->getDomain($this->username);
		if (is_array($t_domain)){
			$this->username = $t_domain['username'];
			$this->domain = $t_domain['domain'];
		}elseif($_POST['openotp_domain'] != NULL) $this->domain = $_POST['openotp_domain'];
		else $this->domain = $t_domain;
	
	
		if ($this->state != NULL) {
			// OpenOTP Challenge
		    $GLOBALS['log']->debug("Sending openotpChallenge request for user '".$this->username);
			$response = $this->openotp_auth->openOTPChallenge($this->username, $this->domain, $this->state, $this->password, $this->u2f);
		} else {
			// OpenOTP Login
		    $GLOBALS['log']->debug("Sending openotpLogin request for user '".$this->username);
			$response = $this->openotp_auth->openOTPSimpleLogin($this->username, $this->domain, utf8_encode($this->password));
		}

		if (!$response || !isset($response['code'])) {
	    	$GLOBALS['log']->error("Invalid OpenOTP response");                                      
			return false;
		}
		
	
		switch ($response['code']) {
		 case 0:
			$GLOBALS['log']->info("OpenOTP login failed for user '".$this->username."' (".$response['message'].")");
			break;
		 case 1:
			$GLOBALS['log']->info("OpenOTP login success for user '".$this->username."' (".$response['message'].")");
			$GLOBALS['log']->debug("OpenOTP challenge timeout for user '".$this->username."' is ".$response['timeout']." seconds");
			
			// check user exists in database
			$dbresult = $GLOBALS['db']->query("SELECT id, status FROM users WHERE user_name='".$this->username."' AND deleted=0");
			if (!$dbresult) {
				$GLOBALS['log']->info("Login failed for user '$name' (SQL query failed)");
				return false;
			}
			$row = $GLOBALS['db']->fetchByAssoc($dbresult);
			if (!$row) {
			if ($GLOBALS['ldap_config']->settings['ldap_auto_create_users']) { 
				$GLOBALS['log']->info("Creating user '".$this->username."' in Sugar DB)");
				return $this->createUser($this->username);
			} else {
				$GLOBALS['log']->info("Login failed for user '".$this->username."' (user not found in Sugar DB)");
				return false;
			}
			}
			if ($row['status'] == 'Inactive') {
				$GLOBALS['log']->info("Login failed for user '".$this->username."' (user inactive in Sugar DB)"); 
				return false;
			}
			$this->loadUserOnSession($row['id']);
			return $row['id'];
			break;
		 case 2:
			$GLOBALS['log']->info("OpenOTP login challenge for user '".$this->username."' (".$response['message'].")");
			$otp_script = $this->openotp_auth->getOverlay($response['otpChallenge'], $response['u2fChallenge'], $response['message'], $this->username, $response['session'], $response['timeout'], $this->password,"modules/Users/authentication/OpenOTPAuthenticate", $this->domain);			
			$jsscript = "<script type='text/javascript' src='chrome-extension://pfboblefjcgdjicmnffhdgionmgcdmne/u2f-api.js'></script>"."\r\n";
			$jsscript .= "<script type='text/javascript'>".$otp_script."</script>";
			$_SESSION['openotp_overlay'] = $jsscript;
			break;
		 default:
			$GLOBALS['log']->error("Invalid OpenOTP response code ".$response['code']);
			break;
		}
		
	return false;
    }
}

?>
